package com.easylinkin.linkappapi.redis;

import com.easylinkin.linkappapi.common.utils.DbPasswordCallback;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @author ouyang
 * 基于redis的同步器，可以实现资源的申请与释放
 */
@Component
public class RedisSync {

    // 锁名称
    public static final String LOCK_PREFIX = "redis_lock:";

    public static final String LOCK_END = "_redissync_end";

    private static final Logger LOGGER = LoggerFactory.getLogger(DbPasswordCallback.class);


    @Autowired
    private StringRedisTemplate redisTemplate;

    static final Map<String, String> lockKeys = new ConcurrentHashMap<>();
    static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
            .setNameFormat("redisSync-pool-%d").build();
    static ScheduledExecutorService ex = new ScheduledThreadPoolExecutor(1, namedThreadFactory);

    /**
     * 更新锁的生存时间，防止生存时间到了任务还没做完的情况
     */
    @PostConstruct
    public void init() {
        ex.scheduleAtFixedRate(() -> {
            if (!CollectionUtils.isEmpty(lockKeys)) {
                Iterator<Map.Entry<String, String>> iterator = lockKeys.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, String> next = iterator.next();
                    String key = next.getKey();
                    redisTemplate.expire(key, 10, TimeUnit.MINUTES);
                }
            }
        }, 1, 5, TimeUnit.MINUTES);
    }

    /**
     * 申请成功执行callback，
     * @param holdTime 持有锁时间
     * @param autoRelease 执行完成自动释放
     */
    public void tryAcquireRelease(String key, long holdTime, boolean autoRelease, Runnable callback) {
        if (this.tryAcquire(key, holdTime)) {
            try {
                callback.run();
            } catch (Exception e) {
                LOGGER.error("redis设置原子操作失败啦:", e);
            } finally {
                if (autoRelease) {
//               此处如果释放了,其他节点就可以再次进入了
                    this.release(key);
                }
            }
        }
    }

    /**
     * 释放
     */
    public void release(String key) {
        String lock = LOCK_PREFIX + key + LOCK_END;
        lockKeys.remove(lock);
        redisTemplate.delete(lock);
    }


    /**
     * 申请
     *
     * @param key      key
     * @param holdTime 持有时间，此时间内只能一次获取锁
     */
    public boolean tryAcquire(String key, long holdTime) {
        String lockKey = LOCK_PREFIX + key + LOCK_END;
        String value = String.valueOf(System.currentTimeMillis());
        Boolean statusCode = redisTemplate.execute(connection ->
                connection.setNX(lockKey.getBytes(), value.getBytes()), true);

        if (statusCode != null && statusCode) {
            System.out.println(lockKey);
            redisTemplate.expire(lockKey, holdTime, TimeUnit.MILLISECONDS);
            lockKeys.put(lockKey, lockKey);
            return true;
        }

        return false;
    }

}
